package pt.uma.gap.accessperm;

import java.io.File;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;



/** This class reads the XML file and generates the access permissions. The generated access permissions
 are stored in the same XML file. The input XML file contains the information about nodes and
 edges whereas the generated output XML file also contains the details of the generated access permissions
*/

/**
 * @author Ijaz Ijaz Ahmed
 * @since 24/1/2011
 */

public class GAP {

	/**
	 * @param args
	 */

	private final static int UNIQUE=1; 
	private final static int FULL=2; 
	private final static int SHARE=3; 
	private final static int PURE=4; 
	private final static int IMMUTEABLE=5; 
	private final static int PRE=0; 
	private final static int POST=1; 

	//static ArrayList m_pointeeNonLocalList = new ArrayList();
	static String m_xPathforMethod;
	static Document m_doc;
	static String m_filePtahatHardDisk;
	static String m_filePtahTarget;


	public static void main(String[] args) 
	{
//		m_filePtahatHardDisk="/Users/ijazahmed/Documents/workspace/GAP/src/input.xml";
//		m_filePtahTarget="/Users/ijazahmed/Documents/workspace/GAP/src/output.xml";
		
		m_filePtahatHardDisk = "C://Wokspace//Programs//eclipse//graph.xml";
		m_filePtahTarget = "C://Wokspace//Programs//eclipse//graph_out.xml";
		
		
		File file = new File(m_filePtahatHardDisk);

		try{
			DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = fact.newDocumentBuilder();
			m_doc = (Document) builder.parse(file);
			generateAccessPermisisonsForMethod(m_doc,"null","Test","alias_write(Object,Object,Object)");
			writeXmlFile(m_doc,m_filePtahTarget);	
		}

		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	

	/**
	 * This is a function that is used to generate access permissions for the method
	 * 
	 * @param doc
	 * @param strpackage
	 * @param strclass
	 * @param strMethod
	 * @throws TransformerException
	 */
	public static void generateAccessPermisisonsForMethod(Document doc, String strpackage, String strclass,String strMethod) throws TransformerException
	{
		m_doc= doc;
		m_xPathforMethod="/root/package/classes/class[@name= '"+strclass+"']/methods/method[@name= '"+strMethod+"']";
		String MEMXPath=m_xPathforMethod+"/variables/variable[@type='MEM']";

		NodeList nodelistMEM = org.apache.xpath.XPathAPI.selectNodeList(doc, MEMXPath);

		for (int i=0; i<nodelistMEM.getLength();i++)
		{
			String memObject=nodelistMEM.item(i).getAttributes().getNamedItem("name").getNodeValue();
			FindAccessPermisisonsForMemoryObject(memObject);
		}

	}
	private static void FindAccessPermisisonsForMemoryObject(String memObject)
	{

		boolean contextRead=false;boolean contextWrite=false;boolean fooRead=false; boolean fooWrite=false;
		// These boolean variables used to check that either an edge exists from foo and context or not.
		int numberOfPointees=0;  // This variable is used to store the number of references pointing to a certain object

		// these string variables are used to store the XPath. The objective is to check either particular edge exist or not
		String readContextXPath=m_xPathforMethod+"/edges/edge[@from='Context' and @to ='"+memObject+"' and @type='R' ]";
		String writeContextXPath=m_xPathforMethod+"/edges/edge[@from='Context' and @to ='"+memObject+"' and @type='W' ]";
		String readFooXPath=m_xPathforMethod+"/edges/edge[@from='Foo' and @to ='"+memObject+"' and @type='R' ]";
		String writeFooXPath=m_xPathforMethod+"/edges/edge[@from='Foo' and @to ='"+memObject+"' and @type='W' ]";
		String pointeeXPath=m_xPathforMethod+"/edges/edge[@to ='"+memObject+"' and @type='P' ]";

		try {
			NodeList nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, readContextXPath);
			if (nodelist.getLength()==1)
				contextRead=true;

			nodelist=null;
			nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, writeContextXPath);
			if (nodelist.getLength()==1)
				contextWrite=true;

			nodelist=null;
			nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, readFooXPath);
			if (nodelist.getLength()==1)
				fooRead=true;

			nodelist=null;
			nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, writeFooXPath);
			if (nodelist.getLength()==1)
				fooWrite=true;

			nodelist=null;
			nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, pointeeXPath);
			numberOfPointees=nodelist.getLength();

			
			int accessPermission=calculateAccessPermisisonForMemoryObject(contextRead,contextWrite,fooRead,fooWrite);
			calculatePreAccessPermisisons(memObject,accessPermission);
			ArrayList pointeeNonLocalList = new ArrayList();
			findNonLocalPointees(nodelist,pointeeNonLocalList);
			calculatePostAccessPermisisonsForPointees(memObject, accessPermission,pointeeNonLocalList);
			

		} 
		catch (Exception e) {

			e.printStackTrace();
		}

	}


	private static void calculatePreAccessPermisisons(String memObject,int accessPermission) throws TransformerException {
		
		String pointeeName=memObject.substring(0, memObject.length()-4);// remove _Mem to find the pointee name
		String pointeeNonLocalXPath=m_xPathforMethod+"/variables/variable[@name ='"+pointeeName+"' and @origion='LOCAL' ]";
		NodeList nonLocal = org.apache.xpath.XPathAPI.selectNodeList(m_doc, pointeeNonLocalXPath);
		if (nonLocal.getLength()==0)
			assignAccessPermisisonsToOnePointee(0,pointeeName,accessPermission);
			
		
	}

	private static void findNonLocalPointees(NodeList nodelist, ArrayList pointeeNonLocalList ) throws TransformerException
	{
		for (int i=0;i<nodelist.getLength();i++)
		{
			Node node=nodelist.item(i);

			NamedNodeMap map=node.getAttributes();
			Node att=map.getNamedItem("from");
			String pointeeName=att.getNodeValue();

			String pointeeNonLocalXPath=m_xPathforMethod+"/variables/variable[@name ='"+pointeeName+"' and @origion='LOCAL' ]";
			NodeList nonLocal = org.apache.xpath.XPathAPI.selectNodeList(m_doc, pointeeNonLocalXPath);
			if (nonLocal.getLength()==0) // means this is non local references, then add this reference into the bag
				pointeeNonLocalList.add(pointeeName);
		}

	}

	private static int calculateAccessPermisisonForMemoryObject(boolean contextRead, boolean contextWrite, boolean fooRead, boolean fooWrite )
	{
		int accessPermissions=0;
		//1 is used to represent unique, 2 is used to re

		if (contextWrite) // if context writes, no need to check if context reads
		{
			if (fooWrite)
				accessPermissions=SHARE;
			else if (fooRead)
				accessPermissions=PURE;
		}
		else if(contextRead) // if not writes then check if it reads
		{
			if (fooWrite)
				accessPermissions=FULL;
			else if (fooRead)
				accessPermissions=IMMUTEABLE;


		}
		else // no context, Chances to produce Unique permissions
		{
			accessPermissions=UNIQUE;

		}

		return accessPermissions;

	}


	private static void calculatePostAccessPermisisonsForPointees(String memObject,int accessPermisison,ArrayList pointeeNonLocalList){

		int numberOfPointees=pointeeNonLocalList.size();

		if (numberOfPointees==0)
		{
			// do nothing, it means that no one is pointing to that object
		}
		else if (numberOfPointees==1)
		{ 
			// assign the same access permission to the one and only one pointee
			assignAccessPermisisonsToPointees(pointeeNonLocalList,accessPermisison);

		}
		else if (numberOfPointees >1)
		{
			if (accessPermisison==UNIQUE ||accessPermisison==FULL)
			{
					//assign the full permission to one pointee and use pure permissions for others pointees
					String strPointeeName=memObject.substring(0,memObject.length()-4); //remove the _Mem from end
					if (pointeeNonLocalList.contains(strPointeeName))
					{
						assignAccessPermisisonsToOnePointee(1,strPointeeName,FULL);
						pointeeNonLocalList.remove(strPointeeName);
						assignAccessPermisisonsToPointees(pointeeNonLocalList,PURE);
	
					}
					else
					{
						assignAccessPermisisonsToOnePointee(1,(String)pointeeNonLocalList.get(0),FULL);
						pointeeNonLocalList.remove(0);
						assignAccessPermisisonsToPointees(pointeeNonLocalList,PURE);
					}
			}
			else
			{
				assignAccessPermisisonsToPointees(pointeeNonLocalList,accessPermisison);
			}

		} // end of if (numberOfPointees >1)
	}

	private static void assignAccessPermisisonsToPointees(ArrayList pointeeNonLocalList, int accessPermisison)
	{
		for (int i=0; i<pointeeNonLocalList.size();i++)
			assignAccessPermisisonsToOnePointee(1,(String)pointeeNonLocalList.get(i),accessPermisison);

	}

	private static void assignAccessPermisisonsToOnePointee(int type, String pointeeName, int accessPermisison)
	{
		String pointeeNonLocalXPath=m_xPathforMethod+"/variables/variable[@name ='"+pointeeName+"']";
		
		try{
			NodeList nodelist = org.apache.xpath.XPathAPI.selectNodeList(m_doc, pointeeNonLocalXPath);
			int numberOfPointees=nodelist.getLength();
			if (numberOfPointees!=0)
			{
				Element element=(Element)nodelist.item(0);
				String strAccessPermision=getAccessPermissionString(accessPermisison);
				String strType=getAccessPermissionType(type);
				element.setAttribute(strType,strAccessPermision);
			}
			}
		catch(Exception e)
		{
			e.printStackTrace();

		}
	}

	private static String getAccessPermissionString(int accessPermission)
	{
		String strAccessPermission=null;
		if (accessPermission==UNIQUE)
				strAccessPermission="unique";
		else if (accessPermission==FULL)
				strAccessPermission= "full";
		else if (accessPermission==SHARE)
				strAccessPermission= "share";
		else if (accessPermission==PURE)
				strAccessPermission= "pure";
		else if (accessPermission==IMMUTEABLE)
				strAccessPermission= "immutable";
		return strAccessPermission;
	}
	
	private static String getAccessPermissionType(int accessPermissionType)
	{
		String strAccessPermissionType=null;
		if (accessPermissionType==PRE)
			strAccessPermissionType="preAccessPermission";
		else if (accessPermissionType==POST)
			strAccessPermissionType= "postAccessPermission";
		
		return strAccessPermissionType;
	}
	
	private static void writeXmlFile(Document doc, String filename) throws Exception {


		// Prepare the DOM document for writing Source
		DOMSource source = new DOMSource(doc);
		// Prepare the output file
		File file = new File(filename);
		Result result = new StreamResult(file.toURI().getPath());
		// Write the DOM document to the file
		Transformer xformer = TransformerFactory.newInstance().newTransformer();

		xformer.transform(source, result);

		//Debug
		System.out.println("XML created: "+file.toURI().getPath());

	}
}
